#include <stm32f4xx_ll_gpio.h>
#include "stm32f4xx_ll_bus.h"
#include "stm32f4xx_ll_system.h"
#include <stm32f4xx_hal_i2c.h>

#include "unios.h"
#include "_mcu_clock.h"
#include "_mcu_gpio.h"
#include "_mcu_i2c.h"

// 初始化 I2C 使用的 GPIO 引脚
static std_err_t _i2c_init_gpio(I2C_TypeDef *port, mcu_gpio_num_t _sclpin, mcu_gpio_num_t _sdapin) {
    GPIO_TypeDef *sclport = NULL, *sdaport = NULL;
    uint32_t sclpin, sdapin;

    switch((uintptr_t)port) {
        case (uintptr_t)I2C1:
            switch(_sclpin) {
                case MCU_GPIO_NUM_PB6 : { sclport = GPIOB; sclpin = LL_GPIO_PIN_6 ; LL_GPIO_SetAFPin_0_7 (sclport, sclpin, LL_GPIO_AF_4); break; }
                case MCU_GPIO_NUM_PB8 : { sclport = GPIOB; sclpin = LL_GPIO_PIN_8 ; LL_GPIO_SetAFPin_8_15(sclport, sclpin, LL_GPIO_AF_4); break; }
                default: return STD_ERR_INVALID_ARG;
            }
            switch(_sdapin) {
                case MCU_GPIO_NUM_PB7 : { sdaport = GPIOB; sdapin = LL_GPIO_PIN_7 ; LL_GPIO_SetAFPin_0_7 (sdaport, sdapin, LL_GPIO_AF_4); break; }
                case MCU_GPIO_NUM_PB9 : { sdaport = GPIOB; sdapin = LL_GPIO_PIN_9 ; LL_GPIO_SetAFPin_8_15(sdaport, sdapin, LL_GPIO_AF_4); break; }
                default: return STD_ERR_INVALID_ARG;
            }
            break;
        case (uintptr_t)I2C2:
            switch(_sclpin) {
                case MCU_GPIO_NUM_PB10: { sclport = GPIOB; sclpin = LL_GPIO_PIN_10; LL_GPIO_SetAFPin_8_15(sclport, sclpin, LL_GPIO_AF_4); break; }
                case MCU_GPIO_NUM_PF1 : { sclport = GPIOF; sclpin = LL_GPIO_PIN_1 ; LL_GPIO_SetAFPin_0_7 (sclport, sclpin, LL_GPIO_AF_4); break; }
                case MCU_GPIO_NUM_PH4 : { sclport = GPIOH; sclpin = LL_GPIO_PIN_4 ; LL_GPIO_SetAFPin_0_7 (sclport, sclpin, LL_GPIO_AF_4); break; }
                default: return STD_ERR_INVALID_ARG;
            }
            switch(_sdapin) {
                case MCU_GPIO_NUM_PB11: { sdaport = GPIOB; sdapin = LL_GPIO_PIN_11; LL_GPIO_SetAFPin_8_15(sdaport, sdapin, LL_GPIO_AF_4); break; }
                case MCU_GPIO_NUM_PF0 : { sdaport = GPIOF; sdapin = LL_GPIO_PIN_0 ; LL_GPIO_SetAFPin_0_7 (sdaport, sdapin, LL_GPIO_AF_4); break; }
                case MCU_GPIO_NUM_PH5 : { sdaport = GPIOH; sdapin = LL_GPIO_PIN_5 ; LL_GPIO_SetAFPin_0_7 (sdaport, sdapin, LL_GPIO_AF_4); break; }
                default: return STD_ERR_INVALID_ARG;
            }
            break;
        case (uintptr_t)I2C3:
            switch(_sclpin) {
                case MCU_GPIO_NUM_PA8 : { sclport = GPIOA; sclpin = LL_GPIO_PIN_8 ; LL_GPIO_SetAFPin_8_15(sclport, sclpin, LL_GPIO_AF_4); break; }
                case MCU_GPIO_NUM_PH7 : { sclport = GPIOH; sclpin = LL_GPIO_PIN_7 ; LL_GPIO_SetAFPin_0_7 (sclport, sclpin, LL_GPIO_AF_4); break; }
                default: return STD_ERR_INVALID_ARG;
            }
            switch(_sdapin) {
                case MCU_GPIO_NUM_PC9 : { sdaport = GPIOC; sdapin = LL_GPIO_PIN_9 ; LL_GPIO_SetAFPin_8_15(sdaport, sdapin, LL_GPIO_AF_4); break; }
                case MCU_GPIO_NUM_PH8 : { sdaport = GPIOH; sdapin = LL_GPIO_PIN_8 ; LL_GPIO_SetAFPin_8_15(sdaport, sdapin, LL_GPIO_AF_4); break; }
                default: return STD_ERR_INVALID_ARG;
            }
            break;
        default: return STD_ERR_INVALID_ARG;
    }

    // 配置 GPIO 为开漏模式
    LL_GPIO_SetPinMode      (sclport, sclpin, LL_GPIO_MODE_ALTERNATE);
    LL_GPIO_SetPinOutputType(sclport, sclpin, LL_GPIO_OUTPUT_OPENDRAIN);
    LL_GPIO_SetPinPull      (sclport, sclpin, LL_GPIO_PULL_UP);
    LL_GPIO_SetPinSpeed     (sclport, sclpin, LL_GPIO_SPEED_FREQ_HIGH);

    LL_GPIO_SetPinMode      (sdaport, sdapin, LL_GPIO_MODE_ALTERNATE);
    LL_GPIO_SetPinOutputType(sdaport, sdapin, LL_GPIO_OUTPUT_OPENDRAIN);
    LL_GPIO_SetPinPull      (sdaport, sdapin, LL_GPIO_PULL_UP);
    LL_GPIO_SetPinSpeed     (sdaport, sdapin, LL_GPIO_SPEED_FREQ_HIGH);

    return STD_ERR_OK;
}

std_err_t mcu_i2c_init(mcu_i2c_t* dev, mcu_gpio_num_t sclpin, mcu_gpio_num_t sdapin, uint32_t speed) {
	if(!dev || !dev->port) { return STD_ERR_INVALID_ARG; }
    
    // 初始化 I2C 使用的 GPIO 引脚
    _i2c_init_gpio(dev->port, sclpin, sdapin);

	// I2C 复位一下，以确保状态正常?
	// 配置完成后，不能随便复位，否则前面配置的参数会被复位 !!!
	mcu_i2c_reset(dev);
    
    // 配置 I2C 参数（100kHz标准模式）
    LL_I2C_Disable(dev->port);
    //LL_I2C_DisableOwnAddress2(dev->port);
    LL_I2C_SetPeriphClock(dev->port, mcu_clock_get_apb2_freq()); // 设置 I2C 的总线时钟频率 = APB2 总线时钟频率（I2C 挂在 APB2 总线上）
	LL_I2C_SetClockSpeedMode(dev->port, LL_I2C_CLOCK_SPEED_STANDARD_MODE);
    LL_I2C_SetMode(dev->port, LL_I2C_MODE_I2C);
    LL_I2C_Enable(dev->port);
	
    return STD_ERR_OK;
}

std_err_t mcu_i2c_reset(mcu_i2c_t* dev) {
	if(!dev || !dev->port) { return STD_ERR_INVALID_ARG; }
	LL_I2C_EnableReset(dev->port);
	mcu_clock_us_delay(1);
	LL_I2C_DisableReset(dev->port);
	return STD_ERR_OK;
}

std_err_t mcu_i2c_debug(mcu_i2c_t* dev, bool enable) {
	if(!dev || !dev->port) { return STD_ERR_INVALID_ARG; }
	uint32_t periph;
	switch((uintptr_t)dev->port) {
		case (uintptr_t)I2C1: periph = LL_DBGMCU_APB1_GRP1_I2C1_STOP; break;
		case (uintptr_t)I2C2: periph = LL_DBGMCU_APB1_GRP1_I2C2_STOP; break;
		case (uintptr_t)I2C3: periph = LL_DBGMCU_APB1_GRP1_I2C3_STOP; break;
		//case (uintptr_t)I2C4: periph = LL_DBGMCU_APB1_GRP1_I2C4_STOP; break;
		default: return STD_ERR_INVALID_ARG;
	}	
	
	enable ? LL_DBGMCU_APB1_GRP1_FreezePeriph(periph) : LL_DBGMCU_APB1_GRP1_UnFreezePeriph(periph);
	return STD_ERR_OK;
}

std_err_t mcu_i2c_master_trx(mcu_i2c_t* dev, bool start, bool stop, uint32_t txlen, const void* _txdata, size_t rxlen, void* _rxdata) {
	if(!dev || !dev->port || !txlen || !_txdata) { return STD_ERR_INVALID_ARG; }
    uint8_t* txdata = (uint8_t*)_txdata;
    uint8_t* rxdata = (uint8_t*)_rxdata;
	uint32_t time;
	std_err_t err = STD_ERR_OK;
	
	// 停止监听
	//mcu_i2c_slave_stop_listen(dev);

	do {
        if(start) {
            // Send START condition
            LL_I2C_GenerateStartCondition(dev->port);
            unios_wait_us_until(time, 1000, LL_I2C_IsActiveFlag_SB(dev->port)); // 确保 Start 条件已产生
            if(time == 0) { err = STD_ERR_TIMEOUT; break; }
        }

		// 发送数据
		for(size_t i=0; i < txlen; i++) {
            LL_I2C_TransmitData8(dev->port, txdata[i]);
			unios_wait_us_until(time, 10000, LL_I2C_IsActiveFlag_TXE(dev->port)); // 确保 发送完成
            if(time == 0) { err = STD_ERR_TIMEOUT; break; }
		}

		if(rxlen) {
            // 确保 地址已经发送完成，并且对端已经 ACK
			unios_wait_us_until(time, 1000, LL_I2C_IsActiveFlag_ADDR(dev->port)); // 
            LL_I2C_ClearFlag_ADDR(dev->port);

            // 接收数据
            for(size_t i=0; i < rxlen; i++) {
                // 除了最后一个字节以外，每接收一个字节都要 ACK
                LL_I2C_AcknowledgeNextData(dev->port, (i != (rxlen - 1)) ? LL_I2C_ACK : LL_I2C_NACK);
                // 等待数据接收完成          
                unios_wait_us_until(time, 1000, LL_I2C_IsActiveFlag_RXNE(dev->port));
                if(time == 0) { err = STD_ERR_TIMEOUT; break; }
                // 保存收到的数据
                rxdata[i] = LL_I2C_ReceiveData8(dev->port);
            }
        }
            
        if(stop) {
            // 生成停止条件
            LL_I2C_GenerateStopCondition(dev->port);
        }
	} while(0);
    
	if(err) {
		// !!! 注意: 如果遇到错误，必须强行生成停止条件，以释放总线控制权。否则可能导致总线锁死 !!!
		LL_I2C_GenerateStopCondition(dev->port); 
		// 不能随便复位，否则前面配置的参数会被复位 !!!
		//mcu_i2c_reset(dev);
	}
	
	return err;
}
